// This is P4 sample source for basic_switching
#include <tofino/constants.p4>
#include <tofino/intrinsic_metadata.p4>
#include <tofino/primitives.p4>
#include <tofino/pktgen_headers.p4>
#include <tofino/stateful_alu_blackbox.p4>
#include <tofino/wred_blackbox.p4>
#include "includes/headers.p4"
#include "includes/parser.p4"
#include "includes/macro.p4"
#include "includes/tofino.p4"
#define CPU_PORT 192
#define TCAM_SIZE 1024

counter tcache_counter{
  type: packets;
  static: cnt_drop_t;
  instance_count: TCAM_SIZE;
  saturating;
}
/* action cnt_drop(index) { */
/*     count(tcache_counter, index); */
/*     drop(); */
/* } */
action cnt_drop() {
    count(tcache_counter, ndn_metadata.index);
    drop();
}


table cnt_drop_t {
  actions {
    cnt_drop;
  }
  default_action: cnt_drop;
}

action get_mask(maskBits){
  modify_field(ndn_metadata.maskBits, maskBits);
}

table mask_t {
  reads{ndn_metadata.component:ternary;} //**0x0到**0x7f
  actions {
    get_mask;
  }
  default_action: get_mask(0);
}

action get_group(group){
  modify_field(ndn_metadata.group, group);
}

table group_t {
  reads{ndn_metadata.component:ternary;} //0x0**到0x3**
  actions {
    get_group;
  }
  default_action: get_group(0);
}

action get_component7(){
  modify_field(ndn_metadata.component, ndn.name7);
}
table component7_t {
  actions {get_component7;}
  default_action:get_component7;
}

action get_component6(){
  modify_field(ndn_metadata.component, ndn.name6);
}
table component6_t {
  actions {get_component6;}
  default_action:get_component6;
}

action get_component5(){
  modify_field(ndn_metadata.component, ndn.name5);
}
table component5_t {
  actions {get_component5;}
  default_action:get_component5;
}

action get_component4(){
  modify_field(ndn_metadata.component, ndn.name4);
}
table component4_t {
  actions {get_component4;}
  default_action:get_component4;
}

action get_component3(){
  modify_field(ndn_metadata.component, ndn.name3);
}
table component3_t {
  actions {get_component3;}
  default_action:get_component3;
}


action check_hit(index, prefixLen, bitmap0, bitmap1, bitmap2, bitmap3){
  modify_field(ndn_metadata.prefixLen, prefixLen);
  modify_field(ndn_metadata.bitmap0, bitmap0);
  modify_field(ndn_metadata.bitmap1, bitmap1);
  modify_field(ndn_metadata.bitmap2, bitmap2);
  modify_field(ndn_metadata.bitmap3, bitmap3);
  modify_field(ndn_metadata.index, index);
}

action fwd_cpu(){
  modify_field(ig_intr_md_for_tm.ucast_egress_port, CPU_PORT);
}

table fwd_cpu_t {
  actions {
    fwd_cpu;
  }
  default_action: fwd_cpu;
}

table tcache_tbl {
  reads {
    ndn.name0: ternary;
    ndn.name1: ternary;
    ndn.name2: ternary;
    ndn.name3: ternary;
    ndn.name4: ternary;
    ndn.name5: ternary;
    ndn.name6: ternary;
    ndn.name7: ternary;
  }
  actions {
      check_hit;
      fwd_cpu;
  }
  default_action: fwd_cpu;
  size: TCAM_SIZE;
}


action get_flag0(){
  bit_and(ndn_metadata.maskBits, ndn_metadata.maskBits, ndn_metadata.bitmap0);
}
table flag0_t {
  actions {get_flag0;}
  default_action: get_flag0;
}

action get_flag1(){
  bit_and(ndn_metadata.maskBits, ndn_metadata.maskBits, ndn_metadata.bitmap1);
}
table flag1_t {
  actions {get_flag1;}
  default_action: get_flag1;
}

action get_flag2(){
  bit_and(ndn_metadata.maskBits, ndn_metadata.maskBits, ndn_metadata.bitmap2);
}
table flag2_t {
  actions {get_flag2;}
  default_action: get_flag2;
}

action get_flag3(){
  bit_and(ndn_metadata.maskBits, ndn_metadata.maskBits, ndn_metadata.bitmap3);
}
table flag3_t {
  actions {get_flag3;}
  default_action: get_flag3;
}

control ingress {
    apply(tcache_tbl){
      hit{
        if (ndn_metadata.prefixLen == ndn.len){
          apply(cnt_drop_t);
        }
        else{
          if (ndn_metadata.prefixLen == 7)
            apply(component7_t);
          else if (ndn_metadata.prefixLen == 6)
            apply(component6_t);
          else if (ndn_metadata.prefixLen == 5)
            apply(component5_t);
          else if (ndn_metadata.prefixLen == 4)
            apply(component4_t);
          else
            apply(component3_t);

          apply(mask_t);
          apply(group_t);

          if (ndn_metadata.group == 0)
            apply(flag0_t);
          else if (ndn_metadata.group == 1)
            apply(flag1_t);
          else if (ndn_metadata.group ==2)
            apply(flag2_t);    
          else
            apply(flag3_t);
        
          if (ndn_metadata.maskBits == 0)
            apply(cnt_drop_t);
          else
            apply(fwd_cpu_t);
        }
      }
    } 
}
control egress {}
